package org.nox.helloseries.domain;

import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.WordUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author Nox
 */
public class Serie implements Comparable<Serie>, Cloneable
{

    private static final Logger LOG = LoggerFactory.getLogger( Serie.class );

    private static final String REGEXP_SERIE = "(.*)(?:[Ss](\\d+)[Ee](\\d+)|(\\d+)x(\\d+))(.*)";

    private String nom;

    private int saison = -1;

    private int episode = -1;

    private Boolean active;

    private Version version;

    private Qualite qualite;

    private SerieFileInformation serieFileInformation = new SerieFileInformation();

    /**
     * Constructeur objet Serie
     * 
     * @param s
     * : chaine de caractere non format d'un titre de fichier vidéo
     */
    public Serie( String s )
    {
        this.serieFileInformation.setNomFichier( new String( s ) );
        parseString( s );
    }

    public Serie()
    {
        // Defaut constructor
    }


    private void parseString( String s )
    {
        // Regexp de reconnaissance de série
        Pattern patternSerie = Pattern.compile( REGEXP_SERIE );

        Matcher matcherSerie = patternSerie.matcher( s );

        if ( matcherSerie.matches() )
        {
            // Groupe 1 : Nom de la série, suppréssion des points
            this.nom = formatNomSerie( matcherSerie.group( 1 ) );
            // Groupe 2 à 5 : episode et saison (2 formats)
            if ( matcherSerie.group( 2 ) == null && matcherSerie.group( 3 ) == null )
            {
                this.saison = Integer.parseInt( matcherSerie.group( 4 ) );
                this.episode = Integer.parseInt( matcherSerie.group( 5 ) );
            }
            else
            {
                this.saison = Integer.parseInt( matcherSerie.group( 2 ) );
                this.episode = Integer.parseInt( matcherSerie.group( 3 ) );
            }

            // Parsing version
            Matcher matcherVersion = Pattern.compile( ".*(" + Version.stringValues() + ").*" ).matcher( matcherSerie.group( 6 ) );

            if ( matcherVersion.matches() )
            {
                // VERSION
                this.version = Version.fromValue( matcherVersion.group( 1 ) );
            }
            else
            {
                // Mise en place des valeurs par défaut si non trouvées
                this.version = Version.VO;
            }

            // Parsing qualite
            Matcher matcherQualite = Pattern.compile( ".*(" + Qualite.stringValues() + ").*" ).matcher( matcherSerie.group( 6 ) );

            if ( matcherQualite.matches() )
            {
                // QUALITE
                this.qualite = Qualite.fromValue( matcherQualite.group( 1 ) );
            }
            else
            {
                // Mise en place des valeurs par défaut si non trouvées
                this.qualite = Qualite.SD;
            }

        }
        else
        {
            LOG.error( "Série non reconnue : {}", s );
        }

    }

    public Serie clone()
    {
        Serie o = null;
        try
        {
            o = (Serie) super.clone();
        }
        catch ( CloneNotSupportedException cnse )
        {
        }
        // on renvoie le clone
        return o;
    }

    public String getNom()
    {
        return nom;
    }

    public void setNom( String nom )
    {
        this.nom = formatNomSerie( nom );
    }

    public int getEpisode()
    {
        return episode;
    }

    public void setEpisode( int episode )
    {
        this.episode = episode;
    }

    public int getSaison()
    {
        return saison;
    }

    public void setSaison( int saison )
    {
        this.saison = saison;
    }

    public void setVersion( Version version )
    {
        this.version = version;
    }

    public Qualite getQualite()
    {
        return qualite;
    }

    public void setQualite( Qualite qualite )
    {
        this.qualite = qualite;
    }

    public void setActive( Boolean active )
    {
        this.active = active;
    }

    public Boolean isActive()
    {
        return active;
    }

    public Version getVersion()
    {
        return version;
    }

    public void setSerieFileInformation( SerieFileInformation serieFileInformation )
    {
        this.serieFileInformation = serieFileInformation;
    }

    public SerieFileInformation getSerieFileInformation()
    {
        return serieFileInformation;
    }

    public String toString()
    {
        String st_saison = ( getSaison() < 10 ) ? "0" + getSaison() : "" + getSaison();
        String st_episode = ( getEpisode() < 10 ) ? "0" + getEpisode() : "" + getEpisode();
        return ( getNom() + " S" + st_saison + "E" + st_episode + " " + ( version != null ? version.getValue() : "" ) ).trim();
    }

    /**
     * @param s
     * @return true si s est l'épisode qui suit this
     */
    public boolean isNextEpisode( Serie s )
    {
        if ( this.saison == s.saison )
        {
            return this.episode + 1 == s.episode;
        }
        return false;
    }

    /**
     * @param value
     * @return la chaine value donc la premiere lettre est une majuscule et les autres des minuscules
     */
    public static String formatNomSerie( String value )
    {
        // Chaine random pour "taguer" la zone a ne pas remplacer
        String rand = Long.toString( System.currentTimeMillis() );

        // Capitalisation de la chaine
        value = WordUtils.capitalize( value.trim() );
        // Suppression du dernier point
        value = StringUtils.removeEnd( value, "." );

        // Detection d'un sigle de type X.X.X.X
        Pattern p = Pattern.compile( "(([A-Z])(\\.)+)" );
        Matcher m = p.matcher( value );
        if ( m.find() )
        {
            value = m.replaceAll( "$2" + rand );
        }
        // Suppression des points
        value = value.replaceAll( "\\.", " " );
        // Ajout des points
        value = value.replaceAll( rand, "." );
        // trim
        return value.trim();
    }

    /**
     * @return chaine de caractere formatée pour la recherche du prochain episode de this
     */
    public String nextEpisode()
    {
        String stSaison = ( getSaison() < 10 ) ? "0" + getSaison() : "" + getSaison();
        int next_episode = getEpisode() + 1;
        String stEepisode = ( next_episode < 10 ) ? "0" + next_episode : "" + next_episode;
        return getNom() + " S" + stSaison + "E" + stEepisode + ( version.isVO() ? "" : " " + version.getValue() ) + qualite.getValue();
    }

    /**
     * Fait passer la série à la saison suivante
     */
    public void newSaison()
    {
        this.saison++;
        this.episode = 1;
    }

    @Override
    public boolean equals( Object obj )
    {
        if ( obj instanceof Serie )
        {
            Serie s = (Serie) obj;
            return Objects.equals( nom.toLowerCase(), s.nom.toLowerCase() ) && Objects.equals( episode, s.episode ) && Objects.equals( saison, s.saison ) && Objects.equals( version, s.version )
                && Objects.equals( qualite, s.qualite );
        }
        return false;
    }

    public int compareTo( Serie s )
    {
        int result = ( this.getNom().toLowerCase().compareTo( s.getNom().toLowerCase() ) );
        if ( result == 0 )
        {
            result = Integer.valueOf( this.getSaison() ).compareTo( Integer.valueOf( s.getSaison() ) );
            if ( result == 0 )
            {
                result = Integer.valueOf( this.getEpisode() ).compareTo( Integer.valueOf( s.getEpisode() ) );
            }
        }
        return result;
    }

    public void incEpisode()
    {
        this.episode++;
    }

}
